package com.atguigu.exam.service.impl;


import com.atguigu.exam.entity.ExamRecord;
import com.atguigu.exam.entity.Paper;
import com.atguigu.exam.entity.PaperQuestion;
import com.atguigu.exam.entity.Question;
import com.atguigu.exam.mapper.PaperMapper;
import com.atguigu.exam.mapper.QuestionMapper;
import com.atguigu.exam.service.ExamRecordService;
import com.atguigu.exam.service.PaperQuestionService;
import com.atguigu.exam.service.PaperService;
import com.atguigu.exam.service.QuestionService;
import com.atguigu.exam.vo.AiPaperVo;
import com.atguigu.exam.vo.PaperVo;
import com.atguigu.exam.vo.RuleVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import kotlin.jvm.internal.Lambda;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * 试卷服务实现类
 */
@Slf4j
@Service
public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements PaperService {

    @Autowired
    private QuestionMapper questionMapper;
    @Autowired
    private PaperQuestionService paperQuestionService;
    @Autowired
    private ExamRecordService examRecordService;
    /**
     * 获取试卷详情
     * @param id
     */
    @Override
    public Paper getPaperDetailInfo(Integer id) {
        //1.校验，该试卷是否存在
        Paper paper = getById(id);
        if(paper==null){
            throw new RuntimeException("试卷不存在");
        }
        //2.根据paperid查询题目集合（中间，题目，答案，选项）
        List<Question> questionList = questionMapper.findQuestionsDetailInfo(id);
        //3.校验题目集合 == null -> 赋空集合！ log->做好记录
        if (ObjectUtils.isEmpty(questionList)){
            paper.setQuestions(new ArrayList<>());
            log.warn("试卷中没有题目，试卷ID{}",id);
            return paper;
        }
        //4.对题目进行排序（选择 -> 判断 -> 简答）
        //注意：type排序，是字符类型 -》 字符 -》 对应 -》 固定的数字 1 2 3
        //进行paper题目集合赋值
        questionList.sort((o1,o2)->Integer.compare(typeToInt(o1.getType()),typeToInt(o2.getType())));
        paper.setQuestions(questionList);
        return paper;
    }

    /**
     * 手动选择题目的方式创建试卷
     *
     * @param paperVo
     */
    @Override
    public Paper createPaper(PaperVo paperVo) {
        //1.完善试卷内的信息 名字 描述 考试时间 状态 题目总数 试卷总分数
        Paper paper = new Paper();
        BeanUtils.copyProperties(paperVo,paper);
        paper.setStatus("DRAFT");
        if(ObjectUtils.isEmpty(paperVo.getQuestions())){
            //试卷中没有题目
            paper.setQuestionCount(0);
            paper.setTotalScore(BigDecimal.ZERO);
            save(paper);
            log.warn("本次{}试卷，没有选择题目，没有题目的试卷无法进行考试",paper);
            return paper;
        }
        //计算总分
        BigDecimal totalScore = paperVo.getQuestions().values().stream().reduce(BigDecimal.ZERO,BigDecimal::add);
        paper.setTotalScore(totalScore);
        //设置题目总数
        paper.setQuestionCount(paperVo.getQuestions().values().size());
        //2.完成试卷插入
        save(paper);
        //3.中间表集合插入
        List<PaperQuestion> paperQuestionList = paperVo.getQuestions().entrySet().stream()
                .map(entry -> new PaperQuestion(paper.getId().intValue(), Long.valueOf(entry.getKey()), entry.getValue()))
                .collect(Collectors.toList());
        //4.中间表的插入
        paperQuestionService.saveBatch(paperQuestionList);
        return paper;
    }

    /**
     * AI选择题目方式创建试卷
     * @param aiPaperVo
     */
    @Override
    public Paper aiCreatePaper(AiPaperVo aiPaperVo) {
        //1.实例化Paper，生成paper对象
        Paper paper = new Paper();
        //2.前端发送请求参数给paper赋值 试卷的基本属性赋值并保存（名字 描述 时间 状态）
        BeanUtils.copyProperties(aiPaperVo,paper);
        //2.1试卷初始状态为DRAFT
        paper.setStatus("DRAFT");
        //2.2保存试卷 insert->save()
        save(paper);
        //3.组卷规则下试题选择和中间表的保存 private List<RuleVo> rules;
        //定义局部变量 题目总数 总分数
        int totalCount = 0;
        BigDecimal totalScore = BigDecimal.ZERO;
        //循环遍历rules集合 AI组卷规则列表，定义不同题型的数量、分值等要求
        for (RuleVo rule : aiPaperVo.getRules()) {
            //3.1如果一级分类[CHOICE,JUDGE,TEXT]选择题目数量为0，跳过该一级分类，进入下一个一级分类选择题目(结束本次循环)
            if (rule.getCount()==0){
                //前端传递该类型下选择的题目数量为0
                continue;
            }
            //3.2查询 select->selectList 当前分类下的题目集合，包括一级分类和二级分类（判断是否选择了二级分类）
            LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(Question::getType,rule.getType());
            //二级分类可能有多个也可能没有，加条件判断二级分类是否为空
            lambdaQueryWrapper.in(!rule.getCategoryIds().isEmpty(),Question::getCategoryId,rule.getCategoryIds());
            List<Question> allQuestionList = questionMapper.selectList(lambdaQueryWrapper);
            //3.3校验查询到的题目集合，如果题目集合为空，跳过，进入下一个一级分类选择题目
            if (allQuestionList==null || allQuestionList.isEmpty()){
                continue;
            }
            //3.4判断查询到的题目集合中题目数量够不够前端输入的加入试卷的题目的数量，如果不够，全部都要
            //Math.min(a,b)接收两个参数，返回其中较小的那个值
            int realNumbers = Math.min(allQuestionList.size(), rule.getCount());
            //3.5计算添加的题目数量和总分数
            totalCount += realNumbers;
            totalScore = totalScore.add(BigDecimal.valueOf((long)realNumbers*rule.getScore()));
            //3.6打乱数据，截取需要的题目数量
            Collections.shuffle(allQuestionList);
            List<Question> questionList = allQuestionList.subList(0, realNumbers);
            //3.7转成中间表，进行保存
            Function<Question,PaperQuestion> fun = new Function<Question, PaperQuestion>() {
                @Override
                public PaperQuestion apply(Question question) {
                    PaperQuestion paperQuestion = new PaperQuestion();
                    paperQuestion.setPaperId(paper.getId().intValue());
                    paperQuestion.setQuestionId(question.getId());
                    paperQuestion.setScore(BigDecimal.valueOf(question.getScore()));
                    return paperQuestion;
                }
            };
            List<PaperQuestion> paperQuestionList = questionList.stream().map(fun).collect(Collectors.toList());
            paperQuestionService.saveBatch(paperQuestionList);
        }
        //4.修改试卷信息 update->updateById
        paper.setQuestionCount(totalCount);
        paper.setTotalScore(totalScore);
        updateById(paper);
        return paper;
    }

    /**
     * 更新试卷状态
     * @param id
     * @param status
     */
    @Override
    public void updatePaperStatus(Integer id, String status) {
        //修改目标试卷状态如果为发布，试卷中必须有题目
        if ("PUBLISHED".equals(status)){
            //中间表查询该试卷是否有题目
            LambdaQueryWrapper<PaperQuestion> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(PaperQuestion::getPaperId,id);
            long count = paperQuestionService.count(lambdaQueryWrapper);
            if (count==0){
                throw new RuntimeException("试卷状态修改失败，发布状态的试卷中必须有题目");
            }
        }
        //修改试卷状态
        LambdaUpdateWrapper<Paper> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(Paper::getId,id);
        lambdaUpdateWrapper.set(Paper::getStatus,status);
        update(lambdaUpdateWrapper);
    }

    /**
     * 更新试卷信息
     * @param id
     * @param paperVo
     * @return paper
     * 更新对一条试卷信息 直接更新
     * 更新对多条中间信息 先删除再插入
     */
    @Override
    public Paper updatePaperInfo(Integer id, PaperVo paperVo) {
        //1.校验 如果试卷是发布状态，不能更新
        Paper paper = getById(id);
        if ("PUBLISHED".equals(paper.getStatus())){
            throw new RuntimeException("发布状态下的试卷不允许更新");
        }
        //校验 不同id的试卷，名称不能相同
        LambdaQueryWrapper<Paper> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Paper::getName,paperVo.getName());
        lambdaQueryWrapper.ne(Paper::getId,id);
        long count = count(lambdaQueryWrapper);
        if (count>0){
            throw new RuntimeException("已经存在名称为%s的试卷，请重新修改".formatted(paperVo.getName()));
        }
        //先更新试卷属性[名字 描述 考试时间]
        //1.前端发送请求参数给paper赋值 试卷的基本属性赋值并保存（名字 描述 时间 状态）
        BeanUtils.copyProperties(paperVo,paper);
        //计算总分
        BigDecimal totalScore = paperVo.getQuestions().values().stream().reduce(BigDecimal.ZERO,BigDecimal::add);
        paper.setTotalScore(totalScore);
        //设置题目总数
        paper.setQuestionCount(paperVo.getQuestions().values().size());
        //更新试卷表
        updateById(paper);
        //2.中间表先将该试卷原来包含的题目删除
        LambdaQueryWrapper<PaperQuestion> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
        lambdaQueryWrapper1.eq(PaperQuestion::getPaperId,id);
        paperQuestionService.remove(lambdaQueryWrapper1);
        //3.中间表集合插入
        List<PaperQuestion> paperQuestionList = paperVo.getQuestions().entrySet().stream()
                .map(entry -> new PaperQuestion(paper.getId().intValue(), Long.valueOf(entry.getKey()), entry.getValue()))
                .collect(Collectors.toList());
        paperQuestionService.saveBatch(paperQuestionList);
        return paper;
    }

    /**
     * 删除试卷
     *
     * @param id
     */
    @Override
    public void removePaperById(Integer id) {
        //1.查看状态，如果是发布状态，则不能删除
        Paper paper = getById(id);
        if (paper==null || "PUBLISHED".equals(paper.getStatus())){
            throw new RuntimeException("试卷为发布状态，不能删除");
        }
        //2.查看考试记录表，如果考试记录表中有该试卷记录，不能删除
        LambdaQueryWrapper<ExamRecord> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ExamRecord::getExamId,id);
        long count = examRecordService.count(lambdaQueryWrapper);
        if (count>0){
            throw new RuntimeException("当前试卷%s关联%s条考试记录，该试卷不能删除".formatted(id,count));
        }
        //3.删除试卷表
        removeById(Long.valueOf(id));
        //4.删除中间表中的记录 delete from paper_question where paper_id = id
        paperQuestionService.removeById(Long.valueOf(id));
    }

    private int typeToInt(String type) {
        switch(type){
            case "CHOICE":return 1;
            case "JUDGE":return 2;
            case "TEXT":return 3;
            default:return 4;
        }
    }
} 